/*****************************************************************************
 *   Copyright (C) 2008 John Schember <john@nachtimwald.com>                 *
 *                                                                           *
 *   This file is part of niwbillmanager.                                    *
 *                                                                           *
 *   niwbillmanager is free software: you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License as          *
 *   published by the Free Software Foundation, either version 3 of the      *
 *   License, or (at your option) any later version.                         *
 *                                                                           *
 *   niwbillmanager is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 *   GNU General Public License for more details.                            *
 *                                                                           *
 *   You should have received a copy of the GNU General Public License       *
 *   along with niwbillmanager. If not, see                                  *
 *   <http://www.gnu.org/licenses/>.                                         *
 *****************************************************************************/

#ifndef DATASTORE_H
#define DATASTORE_H

#include <QDate>
#include <QSet>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QStringList>

#include <billitem.h>
#include <transaction.h>

/**
 * Represents either and undo or redo.
 */
class UndoRedoType
{
    public:
        enum UndoRedoTypes {
            UNDO,
            REDO,
        };

        /**
         * Convert an UndoRedoType to a string.
         * @param undoRedoType The type to check.
         * @return A string representing the type. If the type is unkown an
         * empty string is returned.
         */
        static QString undoRedoTypeToString(UndoRedoType::UndoRedoTypes
            undoRedoType);
};

/**
 * The type of query for finding bill items.
 */
class QueryType
{
    public:
        enum QueryTypes {
            OR,
            AND,
        };

        /**
         * Convert an QueryType to a string.
         * @param queryType The type to check.
         * @return A string representing the type. If the type is unkown an
         * empty string is returned.
         */
        static QString queryTypeToString(QueryType::QueryTypes queryType);
};

/**
 * In memory storage of bill items.
 * Holds all of the bill items in a collection. Also, holds the undo and redo
 * buffers. Changes can be accessed though a transaction log.
 */
class DataStore
{
    public:
        DataStore();
        ~DataStore();

        /**
         * Deletes all bill items, clears the undo buffer, redo buffer and
         * the transaction log.
         * This cannot be reversed.
         */
        void clearCollection();
        /**
         * Adds a new bill item to the collection.
         * @param billItem The bill item to add.
         * @param opening True if the item is being added in association
         * with opening a file. This will not add the item to the transaction
         * log, undo or redo buffers.
         * @return The id created for the bill item.
         * @see undoChange
         */
        QString addBillItem(BillItem billItem, bool opening=false);
        /**
         * Modifies a bill item.
         * This can be reversed by calling undoChange.
         * @param billItem The bill item information to modify. id cannot be
         * changed as it is used to reference the bill item.
         * @return True on success
         * @see undoChange
         */
        bool modifyBillItem(BillItem billItem);
        /**
         * Deletes the bill item specified by id.
         * This can be reversed by calling undoChange.
         * @param billId The id of the bill item.
         * @return True on success
         * @see undoChange
         */
        bool deleteBillItem(QString billId);

        /**
         * Gets the bill item associated with id.
         * Clears any start functions. Sets hasMoreBillItems to false.
         * @param billId The id of the bill item.
         * @param billItem A reference to a bill item to store the retrieved
         * data in.
         * @return True upon success.
         */
        bool getBillItemById(QString billId, BillItem &billItem);
        /**
         * Starts a bill item transaction to retrieve bill items associated
         * with a given list of tags.
         * @param tags The tags to find associated bills with.
         */
        void startGetBillItemsFromTags(QSet<QString> tags);
        /**
         * A convience function to get all bills that do not have an associated
         * tag. The same as running startGetBillItemsFromTags with an empty
         * tag list.
         */
        void startGetBillItemsWithoutTags();
        /**
         * Starts a bill item transaction transaction to retrieve all bill
         * items.
         */
        void startGetAllBillItems();
        /**
         * Starts a bill item transcation to retrieve bill items that matchs
         * the query in any of the specified parameters.
         * @param query What to match bill items with.
         * @param queryType Should the query be found in all fields or any.
         * @param amountDue Search in amount due.
         * @param autoPay Search for auto pay bills.
         * @param nonAutoPay Search non auto pay bills.
         * @param dateDue Search in date due.
         * @param duePast Search for past due bills.
         * @param dueToday Search for bills due today.
         * @param dueFuture Search for bills due in the future.
         * @param payee Search in payee.
         * @param paymentLocation Search in payment location.
         * @param paymentMethod Search in payment method.
         * @param paymentRecurringPeriod Search in payment recurring period.
         * @param recurringOnly Search for recurring bills only.
         * @param nonRecurring Search for non-recurring bills.
         * @param paid Search for paid bills.
         * @param unpaid Search for unpaid bills.
         * @param name Search in the bill name.
         * @param notes Search in notes.
         * @param tags Search in tags.
         */
        void startGetBillItems(QSet<QString> query,
            QueryType::QueryTypes queryType,
            bool amountDue=true, bool autoPay=true, bool nonAutoPay=true,
            bool dateDue=true, bool duePast=true, bool dueToday=true,
            bool dueFuture=true, bool payee=true, bool paymentLocation=true,
            bool paymentMethod=true, bool paymentRecurringPeriod=true,
            bool recurringOnly=true, bool nonRecurring=true, bool paid=true,
            bool unpaid=true, bool name=true, bool notes=true, bool tags=true);
        /**
         * Checks if a bill item transaction transaction has bill items that
         * have not been retrieved.
         * @return True if there are more bill items that can be retrieved.
         */
        bool hasMoreBillItems();
        /**
         * Gets the next bill item in a bill item transaction.
         * @return The next bill item.
         */
        bool getNextBillItem(BillItem &billItem);

        /**
         * Gets all tags used by bill items.
         */
        QSet<QString> getAllTags();
        /**
         * Gets all tags used by a bill item specified by id.
         * @param billId The id of the bill item.
         */
        QSet<QString> getTags(QString billId);

        /**
         * Checks if previous changes can be undone.
         * @return True if changes can be undone.
         */
        bool canUndoChange();
        /**
         * Checks if changes can be redone.
         * @return True if changes can be redone.
         */
        bool canRedoChange();
        /**
         * Undo the last change.
         * @param transaction a reference to store the change in.
         * @return True upon success.
         */
        bool undoChange(Transaction &transaction);
        /**
         * Redo (un-undo) the the last change.
         * @param transaction a reference to store the change in.
         * @return True upon success.
         */
        bool redoChange(Transaction &transaction);

        /**
         * Clears all transactions from the transaction log.
         * This should be called after all transactions are queried and written
         * to disk.
         */
        void clearRawTransactionLog();
        /**
         * Starts a transaction to retrieve all transactions that have taken
         * place.
         */
        void startGetRawTransactionLog();
        /**
         * Checks if a transaction log transaction has more transactions that
         * have not been retrieved.
         * @return True if there are more transactions that can be retrieved.
         */
        bool hasMoreRawTransactions();
        /**
         * Gets the next transaction in a transaction log transaction.
         * @return The next transaction.
         */
        ShortTransaction getNextRawTransaction();

    private:
        /**
         * Generic check to see if a query is for a specific table and if that
         * table has more items that can be retrieved from it.
         * @param table The name of the table to check.
         * @return True if the query has more items.
         */
        bool queryHasMore(const QString &table);

        /**
         * An SQL query to insert a bill item into collection.
         * @param billItem The bill item to insert into collection.
         * @return True if the bill item was successfully inserted.
         */
        bool queryInsertBillItem(const BillItem &billItem);
        /**
         * An SQL query to insert a bill item into collection at a specific
         * location. Location is denoted by the id of the bill item.
         * @param billItem The bill item to insert into collection.
         * @return True if the bill item was successfully inserted.
         */
        bool queryInsertBillItemWithId(const BillItem &billItem);
        /**
         * An SQL query to update a bill item in collection.
         * @param billItem The bill item to update in collection.
         * @return True if the bill item was successfully updated.
         */
        bool queryUpdateBillItem(const BillItem &billItem);
        /**
         * An SQL query to delete a bill item from collection.
         * @param billId The bill id to delete from collection.
         * @return True if the bill item was successfully deleted.
         */
        bool queryDeleteBillItem(const QString &billId);

        /**
         * Add an item to the transaction log.
         * @param transactionType The type of transaction.
         * @param billId The id of the bill item that was added, changed or
         * deleted.
         */
        void addTransactionItem(
            TransactionType::TransactionTypes transactionType,
            const QString &billId);

        /**
         * Add an item to either the undo or redo buffer.
         * @param undoRedoType Which buffer the items should be added to.
         * @param transactionType The type of transaction that is being stored.
         * @param billItem The changes.
         */
        void addUndoRedoItem(UndoRedoType::UndoRedoTypes undoRedoType,
            TransactionType::TransactionTypes transactionType,
            BillItem billItem);
        /**
         * Remove an item from either the undo or redo buffer.
         * @param undoRedoType Which buffer the item should be removed from.
         * @param rowId Where the item is located in the buffer.
         */
        void removeUndoRedoItem(UndoRedoType::UndoRedoTypes undoRedoType,
            QString rowId);
        /**
         * Check if either the undo or redo buffers have items in them.
         * @param undoRedoType Which buffer to check.
         */
        bool canUndoRedoChange(UndoRedoType::UndoRedoTypes undoRedoType);
        /**
         * Run either undo or redo on the collection.
         * @param undoRedoType Which buffer to use.
         * @param transaction A reference to store the change that was made
         * in. This is needed to know how the collection was changed.
         * @return True upon success.
         */
        bool undoRedoChange(const UndoRedoType::UndoRedoTypes &undoRedoType,
            Transaction &transaction);
        /**
         * Get the last item in either the undo or redo buffer.
         * Undo and Redo buffers should be traversed in a first in last out
         * manner.
         * @param undoRedoType Which buffer should be used.
         * @param transaction A reference to store the transaction stored
         * in the buffer.
         * @param rowId A reference to store the location of the item in the
         * buffer.
         * @return True upon success.
         */
        bool getLastUndoRedoItem(UndoRedoType::UndoRedoTypes undoRedoType,
            Transaction &transaction, QString &rowId);
        /**
         * Clear either the undo or redo buffer.
         * @param undoRedoType Which buffer should be cleared.
         */
        void clearUndoRedoBuffer(UndoRedoType::UndoRedoTypes undoRedoType);

        QSqlDatabase m_db;
        QString m_dbName;
        QSqlQuery *m_query;
};

#endif /* DATASTORE_H */
